// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2020. All rights reserved.
 * Generated on: 2020/2/6
 * Function description: adaption related to the PCIe main chip
 */
#include <linux/pci.h>
#include <linux/string.h>
#include "include/mach/pcie_chip.h"
#include "include/mach/pcie_core.h"
#include "include/mach/hw_drv_core.h"

#if KER_F_DESC("global variable definition")
static enum HW_CHIP_ID_E g_pcie_chip_id;
static struct pcie_info g_pcie_ctrl_info[PCIE_INDEX_MAX];
static uint32_t *g_pcie0_lb_test_buffer;
static uint32_t *g_pcie1_lb_test_buffer;
static uint32_t *g_pcie2_lb_test_buffer;
/* pcie iatu table */
static struct pcie_iatu *g_pcie0_iatu_table;
static struct pcie_iatu *g_pcie1_iatu_table;
static struct pcie_iatu *g_pcie2_iatu_table;
static uint32_t g_pcie0_iatu_table_len;
static uint32_t g_pcie1_iatu_table_len;
static uint32_t g_pcie2_iatu_table_len;

void __iomem *g_pcie_mem_base_addr[PCIE_INDEX_MAX] = {0};
EXPORT_SYMBOL_GPL(g_pcie_mem_base_addr);

static bool g_pcie_linkdown_state[PCIE_INDEX_MAX] = { true, true, true };

static void *__iomem g_pcie_misc_base_virt[PCIE_INDEX_MAX];

extern struct outer_cache_fns outer_cache;
extern struct pcie_reg_base_addr *g_pcie_reg_base_addr;

#endif

#if KER_F_DESC("reg operation function")

int32_t clear_reg_iomap_bit(uint32_t addr, uint32_t bit)
{
	uint32_t *reg_addr = NULL;

	reg_addr = (uint32_t *)ioremap(addr, sizeof(uint32_t));
	if (reg_addr == NULL)
		return 1;

	WRITEL_AND((~(1 << bit)), reg_addr);
	iounmap(reg_addr);

	return 0;
}

int32_t clear_reg_iomap_mask(uint32_t addr, uint32_t mask)
{
	uint32_t *reg_addr = NULL;

	reg_addr = (uint32_t *)ioremap(addr, sizeof(uint32_t));
	if (reg_addr == NULL)
		return 1;

	WRITEL_AND((~(mask)), reg_addr);
	iounmap(reg_addr);

	return 0;
}

int32_t set_reg_iomap_mask(uint32_t addr, uint32_t mask)
{
	uint32_t *reg_addr = NULL;

	reg_addr = (uint32_t *)ioremap(addr, sizeof(uint32_t));
	if (reg_addr == NULL)
		return 1;

	WRITEL_ORR(mask, reg_addr);
	iounmap(reg_addr);

	return 0;
}

int32_t clear_reg_iomap_mask_set_val(uint32_t addr, uint32_t mask, uint32_t val)
{
	uint32_t *reg_addr = NULL;
	uint32_t reg_val;

	reg_addr = (uint32_t *)ioremap(addr, sizeof(uint32_t));
	if (reg_addr == NULL)
		return 1;

	/* clear and set */
	reg_val = readl(reg_addr);
	reg_val &= ~mask;
	reg_val |= val;
	writel(reg_val, reg_addr);

	iounmap(reg_addr);

	return 0;
}

int32_t set_reg_iomap_bit(uint32_t addr, uint32_t bit)
{
	uint32_t *reg_addr = NULL;

	reg_addr = (uint32_t *)ioremap(addr, sizeof(uint32_t));
	if (reg_addr == NULL)
		return 1;

	WRITEL_ORR((1 << bit), reg_addr);
	iounmap(reg_addr);

	return 0;
}

int32_t write_reg_iomap_value(uint32_t addr, uint32_t value)
{
	uint32_t *reg_addr = NULL;

	reg_addr = (uint32_t *)ioremap(addr, sizeof(uint32_t));
	if (reg_addr == NULL)
		return 1;

	writel(value, reg_addr);
	iounmap(reg_addr);

	return 0;
}

int32_t read_reg_iomap_value(uint32_t addr, uint32_t *value)
{
	uint32_t *reg_addr = NULL;

	reg_addr = (uint32_t *)ioremap(addr, sizeof(uint32_t));
	if (reg_addr == NULL)
		return 1;

	*value = readl(reg_addr);
	iounmap(reg_addr);

	return 0;
}

void orr_reg_value(void *__iomem reg_addr, uint32_t value)
{
	uint32_t val = readl(reg_addr);

	val |= value;
	writel(val, reg_addr);
}

void clear_reg_bit(void *__iomem reg_addr, uint32_t bit)
{
	uint32_t val = readl(reg_addr);

	val &= ~(1 << bit);
	writel(val, reg_addr);
}

void set_reg_bit(void *__iomem reg_addr, uint32_t bit)
{
	uint32_t val = readl(reg_addr);

	val |= (1 << bit);
	writel(val, reg_addr);
}

int32_t set_gpio_dir_output(uint32_t gpio_num)
{
	struct src_gpio_id_ctrl gpio_id_ctrl = { 0 };
	uint32_t ret;
	struct gpio_drv_func *gpio_drv_func_hook = get_gpio_func_hook();

	/* for 0xFF data and invalid value for xml cfg */
	/* regard as success and return 0 without operation */
	if (gpio_num == PCIE_INVALID_INT8)
		return 0;

	gpio_id_ctrl.ui_id = gpio_num;
	gpio_id_ctrl.ctrl_data.ui_direct = GPIO_DIRECT_OUTPUT;
	/* this sdk will set multiplex mode to gpio mode */
	if ((gpio_drv_func_hook == NULL) ||
	    (gpio_drv_func_hook->gpio_drv_set_direct_hook == NULL))
		return -EIO;

	ret = gpio_drv_func_hook->gpio_drv_set_direct_hook(&gpio_id_ctrl);
	if (ret != HW_RET_SUCCESS)
		return -EIO;

	return 0;
}

int32_t set_gpio_output_value(uint32_t gpio_num, uint32_t value)
{
	uint32_t ret;
	struct src_gpio_id_ctrl gpio_id_ctrl = { 0 };
	struct gpio_drv_func *gpio_drv_func_hook = get_gpio_func_hook();

	if (gpio_num == PCIE_INVALID_INT8)
		return 0;

	gpio_id_ctrl.ui_id = gpio_num;
	gpio_id_ctrl.ctrl_data.ui_value = value;
	if ((gpio_drv_func_hook == NULL) ||
	    (gpio_drv_func_hook->gpio_drv_write_data_hook == NULL))
		return -EIO;

	ret = gpio_drv_func_hook->gpio_drv_write_data_hook(&gpio_id_ctrl);
	if (ret != HW_RET_SUCCESS)
		return -EIO;

	return 0;
}

int32_t set_gpio_drv_ability(uint32_t gpio_num, uint32_t ability)
{
	struct src_gpio_ability gpio_ability = { 0 };
	uint32_t ret;

	struct gpio_drv_func *gpio_drv_func_hook;

	gpio_drv_func_hook = get_gpio_func_hook();
	if (gpio_num == PCIE_INVALID_INT8)
		return 0;

	gpio_ability.pin_id = gpio_num;
	gpio_ability.value  = ability;
	if ((gpio_drv_func_hook == NULL) ||
	    (gpio_drv_func_hook->gpio_drv_set_ability_hook == NULL))
		return -EIO;

	ret = gpio_drv_func_hook->gpio_drv_set_ability_hook(&gpio_ability);
	if (ret != HW_RET_SUCCESS)
		return -EIO;

	return 0;
}

#endif

#if KER_F_DESC("internal function")

static void pcie_dbi_space_door(uint32_t chipid, void *__iomem misc_base_virt, bool is_open)
{
	if (chipid == HAL_CHIP_ID_5182T)
		return;

	if (is_open) {
		set_reg_bit(misc_base_virt + PERI_PCIE7_5116, PCIE_BIT_13);
		return;
	}
	clear_reg_bit(misc_base_virt + PERI_PCIE7_5116, PCIE_BIT_13);
}

bool is_old_soc_chip(void)
{
	return ((g_pcie_chip_id == HW_CHIP_ID_5115H_E) ||
		(g_pcie_chip_id == HW_CHIP_ID_5115T_E) ||
		(g_pcie_chip_id == HW_CHIP_ID_5610T_E));
}

/* init buffer for pcie loopback test */
uint32_t pcie_test_buffer_init(uint32_t **buffer_addr, uint32_t loopback_opt)
{
	if ((buffer_addr == NULL) || (loopback_opt > PCIE_TEST_OPT_ENABLE)) {
		pr_info("test mem buffer addr not init\n");
		return -EINVAL;
	}

	/* enable loopback, apply for space */
	if ((loopback_opt == PCIE_TEST_OPT_ENABLE) && (*buffer_addr == NULL)) {
		*buffer_addr = kmalloc(PCIE_TEST_DATA_LEN, GFP_KERNEL);
		if (*buffer_addr == NULL) {
			pr_info("pcie test mem buffer malloc fail\n");
			return -ENOMEM;
		}
		(void)memset(*buffer_addr, 0, PCIE_TEST_DATA_LEN);
		pr_info("test mem buffer addr =0x%x,  phy_addr=0x%x\n",
			(uint32_t)*buffer_addr, virt_to_phys(*buffer_addr));
		return 0;
	}

	/* disable loopback, release space */
	if ((loopback_opt == PCIE_TEST_OPT_DISABLE) && (*buffer_addr != NULL)) {
		kfree(*buffer_addr);
		*buffer_addr = NULL;
		return 0;
	}

	/* otherwise return fail */
	pr_info("test mem buffer not init\n");
	return -EIO;
}

void pcie_set_iatu_table_5182t(void *__iomem reg_addr,
	struct pcie_iatu *pcie_iatu_table, uint32_t iatu_table_len)
{
	uint32_t i = 0;
	uint32_t channel = 0;
	uint32_t offset = 0;

	void *__iomem iatu_base = reg_addr;

	for (i = 0 ; i < iatu_table_len; i++) {
		channel = pcie_iatu_table[i].viewport & 0xF;

		if (pcie_iatu_table[i].viewport & (1 << 31))
			offset = PCIE_IATU_CFG_INBOUND_OFFSET;
		else
			offset = 0;

		writel(pcie_iatu_table[i].region_ctrl_1,
			iatu_base + offset + PCIE_IATU_CFG_REGION_CTRL1(channel));
		writel(pcie_iatu_table[i].region_ctrl_2,
			iatu_base + offset + PCIE_IATU_CFG_REGION_CTRL2(channel));
		writel(pcie_iatu_table[i].lbar,
			iatu_base + offset + PCIE_IATU_CFG_REGION_LOWER_BASE_ADDR(channel));
		writel(pcie_iatu_table[i].ubar,
			iatu_base + offset + PCIE_IATU_CFG_REGION_UPPER_BASE_ADDR(channel));
		writel(pcie_iatu_table[i].lar,
			iatu_base + offset + PCIE_IATU_CFG_REGION_LIMIT_ADDR(channel));
		writel(pcie_iatu_table[i].ltar,
			iatu_base + offset + PCIE_IATU_CFG_REGION_LOWER_TRGT_ADDR(channel));
		writel(pcie_iatu_table[i].utar,
			iatu_base + offset + PCIE_IATU_CFG_REGION_UPPER_TRGT_ADDR(channel));
	}
}

void download_iatu_table(uint32_t pcie_index, void *__iomem reg_addr)
{
	uint32_t loop;
	struct pcie_iatu *pcie_iatu_table;
	uint32_t pcie_iatu_len;
	uint32_t chipid;

	if (pcie_index == PCIE_INDEX_0) {
		pcie_iatu_table = g_pcie0_iatu_table;
		pcie_iatu_len = g_pcie0_iatu_table_len;
	} else if (pcie_index == PCIE_INDEX_1) {
		pcie_iatu_table = g_pcie1_iatu_table;
		pcie_iatu_len = g_pcie1_iatu_table_len;
	} else if (pcie_index == PCIE_INDEX_2) {
		pcie_iatu_table = g_pcie2_iatu_table;
		pcie_iatu_len = g_pcie2_iatu_table_len;
	} else {
		pr_info("pcie iatu table not init\n");
		return;
	}

	/* itau table addr of 5182t is changed, need special process */
	chipid = pcie_get_chip_id();
	if ((chipid == HAL_CHIP_ID_5182T) || (chipid == HAL_CHIP_ID_1156H) ||
		(chipid == HAL_CHIP_ID_1155H) || (chipid == HAL_CHIP_ID_1156E) ||
		(chipid == HAL_CHIP_ID_2952H)) {
		pcie_set_iatu_table_5182t(reg_addr, pcie_iatu_table, pcie_iatu_len);
		return;
	}

	for (loop = 0; loop < pcie_iatu_len; loop++) {
		writel(pcie_iatu_table[loop].viewport, reg_addr + IATU_VIEWPORT_OFFSET);
		writel(pcie_iatu_table[loop].region_ctrl_1, reg_addr + IATU_REGION_CTRL1_OFFSET);
		writel(pcie_iatu_table[loop].region_ctrl_2, reg_addr + IATU_REGION_CTRL2_OFFSET);
		writel(pcie_iatu_table[loop].lbar, reg_addr + IATU_LBAR_OFFSET);
		writel(pcie_iatu_table[loop].ubar, reg_addr + IATU_UBAR_OFFSET);
		writel(pcie_iatu_table[loop].lar, reg_addr + IATU_LAR_OFFSET);
		writel(pcie_iatu_table[loop].ltar, reg_addr + IATU_LTAR_OFFSET);
		writel(pcie_iatu_table[loop].utar, reg_addr + IATU_UTAR_OFFSET);
	}
}

uint32_t pcie_check_loopback(void *__iomem base_addr,
	void *__iomem misc_base_virt, uint32_t loopback_opt)
{
	uint32_t val;
	uint32_t loop = 0;

	/* 1.set  0x1016101c bit13, select dbi space */
	set_reg_bit(misc_base_virt + PERI_PCIE7_5116, PCIE_BIT_13);

	val = readl(base_addr + DBI_CMD_STATUS_OFFSET);
	val = DBI_SPACE_ENABLE;
	writel(val, base_addr + DBI_CMD_STATUS_OFFSET);

	/* set pcie loopback */
	if (loopback_opt == PCIE_TEST_OPT_ENABLE) {
		set_reg_bit(base_addr + DBI_PORT_LINK_CTRL_OFFSET, PCIE_BIT_2);
		/* loopback status detection */
		do {
			val = readl(base_addr + DBI_DEBUG_REG0_OFFSET);
			if ((val & PCIE_LOOPBACK_STATUS_MASK) == PCIE_LOOPBACK_STATUS_VALUE)
				break;

			udelay(PCIE_LOOPBACK_UDELAY_10US);
		} while (loop++ < TRY_MAX_LOOPBACK_DETECT);

		if ((loop == TRY_MAX_LOOPBACK_DETECT) &&
		    ((val & PCIE_LOOPBACK_STATUS_MASK) != PCIE_LOOPBACK_STATUS_VALUE)) {
			pr_info("remote loopback detect fail\n");
			return -EIO;
		}
	} else {
		clear_reg_bit(base_addr + DBI_PORT_LINK_CTRL_OFFSET, PCIE_BIT_2);
	}
	return 0;
}

void pcie_set_loopback_iatu(uint32_t pcie_port, uint32_t loopback_opt, void *__iomem base_addr)
{
	uint32_t *alig_addr = NULL;

	if (pcie_port == PCIE_INDEX_0) {
		if (loopback_opt == PCIE_TEST_OPT_ENABLE) {
			g_pcie0_iatu_table[IATU_INDEX_2].region_ctrl_1 = 0;
			g_pcie0_iatu_table[IATU_INDEX_2].region_ctrl_2 =
				PCIE_IATU_REGION_CTRL_REG2;
			ADDR_SET(0);
		} else {
			g_pcie0_iatu_table[IATU_INDEX_2].region_ctrl_1 =
				PCIE0_IATU2_REGION_CTRL_REG1;
			g_pcie0_iatu_table[IATU_INDEX_2].region_ctrl_2 =
				PCIE0_IATU_REGION_CTRL_REG2;
			/* recovery init setting */
			g_pcie0_iatu_table[IATU_INDEX_2].ltar = PCIE0_IATU2_LOWER_TARGET_REG;
		}
	}

	if (pcie_port == PCIE_INDEX_1) {
		if (loopback_opt == PCIE_TEST_OPT_ENABLE) {
			ADDR_SET(1);
		} else {
			/* recovery init setting */
			g_pcie1_iatu_table[IATU_INDEX_2].ltar = PCIE1_IATU2_LOWER_TARGET_REG;
		}
	}

	if (pcie_port == PCIE_INDEX_2) {
		if (loopback_opt == PCIE_TEST_OPT_ENABLE) {
			ADDR_SET(2);
		} else {
			/* recovery init setting */
			g_pcie2_iatu_table[IATU_INDEX_2].ltar = PCIE2_IATU2_LOWER_TARGET_REG_5182T;
		}
	}

	download_iatu_table(pcie_port, base_addr);
}

uint32_t pcie_loopback_set(uint32_t pcie_port, uint32_t loopback_opt, uint32_t chip_id)
{
	/* loopback cfg */
	void *__iomem pciex_misc_base_virt;
	uint32_t ret;
	bool is_82t_series = false;
	uint32_t base_dbi_addr = DBI_BASE_ADDR_5182T;

	if ((pcie_port > PCIE_INDEX_2) || (loopback_opt > PCIE_TEST_OPT_ENABLE))
		return -EINVAL;

	pciex_misc_base_virt = g_pcie_misc_base_virt[pcie_port];
	ret = pcie_check_loopback(g_pcie_ctrl_info[pcie_port].dbi_base,
		pciex_misc_base_virt, loopback_opt);
	if (ret != 0)
		return -EIO;

	if (pcie_port == PCIE_INDEX_0)
		ret |= pcie_test_buffer_init(&g_pcie0_lb_test_buffer, loopback_opt);
	else if (pcie_port == PCIE_INDEX_1)
		ret |= pcie_test_buffer_init(&g_pcie1_lb_test_buffer, loopback_opt);
	else if (pcie_port == PCIE_INDEX_2)
		ret |= pcie_test_buffer_init(&g_pcie2_lb_test_buffer, loopback_opt);
	if (ret != 0) {
		pr_err("%s: pcie_test_buffer_init fail\n", __func__);
		return ret;
	}

	/* set iatu table */
	if (chip_id == HAL_CHIP_ID_5182T)
		is_82t_series = true;
	if ((chip_id == HAL_CHIP_ID_1156H) || (chip_id == HAL_CHIP_ID_1155H) ||
		(chip_id == HAL_CHIP_ID_1156E) || (chip_id == HAL_CHIP_ID_2952H)) {
		is_82t_series = true;
		base_dbi_addr = PCIE_FST_N_DBI_BASE_1156;
	}
	if (is_82t_series) {
		void *__iomem iatu_addr;
		uint32_t pcie_port_offset[PCIE_INDEX_MAX] = { 0,
			PCIE1_BASE_REG_OFFSET_5182T, PCIE2_BASE_REG_OFFSET_5182T};

		pr_info("pcie loopback 5182t iatu addr\n");
		iatu_addr = ioremap(base_dbi_addr +
			pcie_port_offset[pcie_port] + PCIE_IATU_CFG_BASE_5182T,
			DBI_SPACE_SIZE);
		if (iatu_addr == NULL) {
			pr_info("pcie loopback 5182t iatu addr map error\n");
			return -EIO;
		}
		pcie_set_loopback_iatu(pcie_port, loopback_opt, iatu_addr);
		iounmap(iatu_addr);
	} else {
		pcie_set_loopback_iatu(pcie_port,
			loopback_opt, g_pcie_ctrl_info[pcie_port].dbi_base);
	}

	/* 0x1016101c bit13, select dbi space */
	clear_reg_bit(pciex_misc_base_virt + PERI_PCIE7_5116, PCIE_BIT_13);
	return ret;
}

uint32_t pcie_loopback_start(uint32_t pcie_port, uint32_t size, uint32_t chip_id)
{
	uint32_t *master_write_addr = NULL;
	uint32_t *slave_write_addr = NULL;

	void *__iomem test_base = NULL;
	uint32_t i = 0;

	if (size == 0) {
		pr_info("size is zero!\n");
		return -EIO;
	}

	if (((g_pcie0_lb_test_buffer == NULL) && (pcie_port == PCIE_INDEX_0)) ||
	    ((g_pcie1_lb_test_buffer == NULL) && (pcie_port == PCIE_INDEX_1)) ||
	    ((g_pcie2_lb_test_buffer == NULL) && (pcie_port == PCIE_INDEX_2))) {
		pr_info("data buffer is NULL!\n");
		return -EIO;
	}

	if (pcie_port == PCIE_INDEX_0)
		PCIE_ADDR_SET(0);
	else if (pcie_port == PCIE_INDEX_1)
		PCIE_ADDR_SET(1);
	else if (pcie_port == PCIE_INDEX_2)
		PCIE_ADDR_SET(2);

	(void)memset(slave_write_addr, 0, size);
	test_base = ioremap((uint32_t)master_write_addr, size);
	if (test_base == NULL) {
		pr_info("pcie loopback test_base map error\n");
		return -EIO;
	}
	for (i = 0; i < size / PCIE_LOOPBACK_PER_DATA_LEN; i++)
		writel(0xAAAA5555, test_base + PCIE_LOOPBACK_PER_DATA_LEN * i);

	flush_cache_all();  // todo: why flush 2 times?
	udelay(PCIE_UDELAY_1000);
	flush_cache_all();

	for (i = 0; i < size / PCIE_LOOPBACK_PER_DATA_LEN; i++) {
		if (*(slave_write_addr++) != 0xAAAA5555) {
			pr_info("data compare fail index=%d data=0x%x\n",
				PCIE_LOOPBACK_PER_DATA_LEN * i, *(slave_write_addr++));
			iounmap(test_base);
			return -EIO;
		}
	}

	pr_info("data compare OK\n");
	iounmap(test_base);
	return 0;
}

bool is_pcie_link_stable(void *__iomem reg_addr, uint32_t state_mask, uint32_t link_value)
{
	uint32_t val;
	uint32_t loop;
	uint32_t link_up_stable_counter;
	bool ret = false;
	uint32_t retry_max_time = TRY_MAX_TIMES;
	struct pcie_board_ini_attr *pcie_board_cfg = NULL;

	pcie_get_board_attr(&pcie_board_cfg);
	if (pcie_board_cfg->pcie_time != PCIE_INVALID_INT32)
		retry_max_time = pcie_board_cfg->pcie_time;

	/* check link up status */
	link_up_stable_counter = 0;
	for (loop = 0; loop < retry_max_time; loop++) {
		val = readl(reg_addr);
		if ((val & state_mask) != link_value) {
			link_up_stable_counter = 0;
			udelay(PCIE_UDELAY_1000);
			continue;
		}

		link_up_stable_counter++;
		if (link_up_stable_counter >= LINK_UP_STABLE_TIMES) {
			ret = true;
			break;
		}
	}
	pr_info("pcie link loop=%d, status=0x%x, max_time:%u\n", loop, val, retry_max_time);
	return ret;
}

/*
 * special process for S5118V2 connected to qualcomm qca9800 wifi chip,
 * avoid qca 9800 pcie impedance to large at high temperature,
 * causing pcie link failed. skip in-position check
 */
void special_handle_5118V2(void *__iomem base_addr, uint32_t pcie_index)
{
	uint32_t *boardid_addr = NULL;
	uint32_t bitvalue = 0;
	uint32_t ret;
	struct gpio_drv_func *gpio_drv_func_hook = get_gpio_func_hook();

	if ((pcie_index == PCIE_INDEX_1) ||
	    (g_pcie_chip_id != HW_CHIP_ID_5118V2_E)) {
		/* origin process only pcie0 need to be processed, keep the same */
		return;
	}

	if ((gpio_drv_func_hook == NULL) ||
	    (gpio_drv_func_hook->gpio_drv_get_sw_bit_hook == NULL))
		return;

	/* 0 for default HW_KER_DEFAULT_CHIPID */
	ret = gpio_drv_func_hook->gpio_drv_get_sw_bit_hook(0, PCIE_SD5118_GPIO68, &bitvalue);
	if (ret != HW_RET_SUCCESS) {
		pr_info("[pcie]don't get Qca9880 sw cfg\n");
		return;
	}

	if (bitvalue == 1) {
		pr_info("LTSSM Enable\n");
		writel(PCIE_SD5118_ULAR_REG_VALUE, base_addr + PCIE_SD5118_ULAR_REG);
		writel(PCIE_SD5118_EXP_ROM_VALUE1, base_addr + PCIE_SD5118_EXP_ROM_BASE_REG);
		writel(PCIE_SD5118_EXP_ROM_VALUE2, base_addr + PCIE_SD5118_EXP_ROM_BASE_REG);
		writel(PCIE_SD5118_EXP_ROM_VALUE3, base_addr + PCIE_SD5118_EXP_ROM_BASE_REG);
		writel(PCIE_SD5118_EXP_ROM_VALUE4, base_addr + PCIE_SD5118_EXP_ROM_BASE_REG);
		writel(PCIE_SD5118_EXP_ROM_VALUE5, base_addr + PCIE_SD5118_EXP_ROM_BASE_REG);
		writel(PCIE_SD5118_EXP_ROM_VALUE6, base_addr + PCIE_SD5118_EXP_ROM_BASE_REG);
		writel(PCIE_SD5118_EXP_ROM_VALUE7, base_addr + PCIE_SD5118_EXP_ROM_BASE_REG);
		writel(PCIE_SD5118_EXP_ROM_VALUE8, base_addr + PCIE_SD5118_EXP_ROM_BASE_REG);
	}
	iounmap(boardid_addr);
}

void force_link_mode(void *__iomem dbi_select_addr,
	void *__iomem dbi_addr, enum pcie_force_link_type link_mode)
{
	uint32_t val;

	/* 0x1016101c bit13 select dbi space */
	if (dbi_select_addr != NULL) {
		/* 0x1016101c bit13 select dbi space */
		set_reg_bit(dbi_select_addr, PCIE_BIT_13);
	}

	/* config pcie1.0 rate */
	val = readl(dbi_addr);
	if (link_mode == PCIE_FORCE_LINK_11)
		val = PCIE_FORCE_LINK_1D1_MODE(val);
	else if (link_mode == PCIE_FORCE_LINK_20)
		val = PCIE_FORCE_LINK_2D0_MODE(val);

	writel(val, dbi_addr);
	/* 0x1016101c bit13 select misc space */
	if (dbi_select_addr != NULL) {
		/* 0x1016101c bit13 select dbi space */
		clear_reg_bit(dbi_select_addr, PCIE_BIT_13);
	}
}

bool pcie_is_force_link_mode(uint32_t pcie_index)
{
	struct pcie_board_ini_attr *pcie_board_cfg = NULL;
	feature_issupport_funcptr *feature_issupport = get_feature_issupport_func_hook();

	pcie_get_board_attr(&pcie_board_cfg);
	if (pcie_board_cfg->pcie_speed == 1) {  /* pcie1.1 speed */
		return true;
	}

	if ((feature_issupport == NULL) || (*feature_issupport == NULL))
		return false;

	if (LINK_MODE_CHECK(0, 10) || LINK_MODE_CHECK(1, 10) || LINK_MODE_CHECK(2, 10))
		return true;

	return false;
}

bool pcie_is_force_link_mode_2D0(uint32_t pcie_index)
{
	feature_issupport_funcptr *feature_issupport;
	struct pcie_board_ini_attr *pcie_board_cfg = NULL;

	pcie_get_board_attr(&pcie_board_cfg);
	if (pcie_board_cfg->pcie_speed == 1)
		return false;

	if (pcie_board_cfg->pcie_speed == 2) /* pcie2.0 speed */
		return true;

	feature_issupport = get_feature_issupport_func_hook();
	if ((feature_issupport == NULL) || (*feature_issupport == NULL))
		return false;

	if (LINK_MODE_CHECK(0, 20) || LINK_MODE_CHECK(1, 20) || LINK_MODE_CHECK(2, 20))
		return true;

	return false;
}

static bool pcie_check_link_mode_82t_2d(void *__iomem reg_addr,
	struct pcie_link_ops_reg *link_ops, uint32_t pcie_index)
{
	uint32_t retry;
	uint32_t val = readl(g_pcie_ctrl_info[pcie_index].dbi_base + PCIE_1D1_LINK_MODE_REG_OFFSET);

	/* configure pcie2.0 rate: bit3:0 = 0x2 */
	set_reg_bit(g_pcie_misc_base_virt[pcie_index] + link_ops->ctrl_reg_7[pcie_index],
		PCIE_BIT_13);

	val = ((val | 0x2) & 0xFFFFFFF2);
	writel(val, g_pcie_ctrl_info[pcie_index].dbi_base + PCIE_1D1_LINK_MODE_REG_OFFSET);

	retry = 0;
	while (retry < 3) {
		/* Retrain Link */
		pr_info("PCIE%u 2.0 Device Retrain %u Link.\n", pcie_index, retry);
		val = readl(g_pcie_ctrl_info[pcie_index].dbi_base + 0x80);
		val = (val | 0x20);
		writel(val, g_pcie_ctrl_info[pcie_index].dbi_base + 0x80);
		if (is_pcie_link_stable(reg_addr,
		    PCIE_2D0_LINK_STATUS_MASK_5182T, PCIE_2D0_LINK_STATUS_5182T)) {
			clear_reg_bit(g_pcie_misc_base_virt[pcie_index] +
				link_ops->ctrl_reg_7[pcie_index],
				PCIE_BIT_13);
			return true;
		}
		retry++;
	}

	clear_reg_bit(g_pcie_misc_base_virt[pcie_index] + link_ops->ctrl_reg_7[pcie_index],
		PCIE_BIT_13);
	return false;
}

int32_t pcie_check_link_mode(uint32_t pcie_index,
	struct pcie_link_ops_reg link_ops, void *__iomem dbi_base_addr)
{
	uint32_t pcie_1d1_link_status_mask;
	bool *pcie_linkdown_stat;
	uint32_t pcie_1d1_link_status;

	void *__iomem reg_addr = NULL;

	if (pcie_index == PCIE_INDEX_0)
		pcie_linkdown_stat = &g_pcie_linkdown_state[PCIE_INDEX_0];
	else if (pcie_index == PCIE_INDEX_1)
		pcie_linkdown_stat = &g_pcie_linkdown_state[PCIE_INDEX_1];
	else
		pcie_linkdown_stat = &g_pcie_linkdown_state[PCIE_INDEX_2];

	reg_addr = g_pcie_misc_base_virt[pcie_index] + link_ops.link_state_reg[pcie_index];
	pcie_1d1_link_status_mask = PCIE_1D1_LINK_STATUS_MASK;
	pcie_1d1_link_status = PCIE_1D1_LINK_STATUS;

	if ((g_pcie_chip_id == HAL_CHIP_ID_5182T) || (g_pcie_chip_id == HAL_CHIP_ID_1156H) ||
		(g_pcie_chip_id == HAL_CHIP_ID_1155H) || (g_pcie_chip_id == HAL_CHIP_ID_1156E) ||
		(g_pcie_chip_id == HAL_CHIP_ID_2952H)) {
		pcie_1d1_link_status_mask = PCIE_1D1_LINK_STATUS_MASK_5182T;
		pcie_1d1_link_status = PCIE_1D1_LINK_STATUS_5182T;
	}

	/* pcie1.1 rate link failed, return in advance, high link rate not continue */
	if (!is_pcie_link_stable(reg_addr, pcie_1d1_link_status_mask, pcie_1d1_link_status)) {
		pr_info("PCIE%d 1.0 Device Link Down.\n", pcie_index);
		*pcie_linkdown_stat = true;
		return -EIO;
	}

	pr_info("PCIE%d 1.1 Device Link Up.\n", pcie_index);
	*pcie_linkdown_stat = false;

	/* 5182t return directly otherwise suspending in 1.1 mode */
	if ((pcie_is_force_link_mode(pcie_index) && (g_pcie_chip_id == HAL_CHIP_ID_5182T)) ||
		(g_pcie_chip_id == HAL_CHIP_ID_1156H) || (g_pcie_chip_id == HAL_CHIP_ID_1155H) ||
		(g_pcie_chip_id == HAL_CHIP_ID_1156E) || (g_pcie_chip_id == HAL_CHIP_ID_2952H))
		return 0;

	/* 1.0 rate to ensure stable timing for pcie connected to lsw */
	if ((!pcie_is_extlsw_chip(PCIE_INDEX_0)) &&
	    ((g_pcie_chip_id == HW_CHIP_ID_5610T_E) ||
	    (!pcie_is_force_link_mode(pcie_index)))) {
		/* 5182t default pcie3.0 rate */
		if ((g_pcie_chip_id == HAL_CHIP_ID_5182T) ||
		    (g_pcie_chip_id == HAL_CHIP_ID_1156H) ||
		    (g_pcie_chip_id == HAL_CHIP_ID_1155H) ||
		    (g_pcie_chip_id == HAL_CHIP_ID_1156E) ||
		    (g_pcie_chip_id == HAL_CHIP_ID_2952H)) {
			if (pcie_is_force_link_mode_2D0(pcie_index)) {
				if (pcie_check_link_mode_82t_2d(reg_addr, &link_ops, pcie_index)) {
					pr_info("PCIE%d 2.0 Device Link Up.\n", pcie_index);
					return 0;
				}
				pr_info("PCIE%d 2.0 Device Link Down.\n", pcie_index);
				return -EIO;
			}

			if (is_pcie_link_stable(reg_addr, PCIE_3D0_LINK_STATUS_MASK_5182T,
				PCIE_3D0_LINK_STATUS_5182T)) {
				pr_info("PCIE%d 3.0 Device Link Up.\n", pcie_index);
				return 0;
			}
			pr_info("PCIE%d 3.0 Device Link Down.\n", pcie_index);
			return -EIO;
		}

		reg_addr = NULL;
		if (link_ops.is_5115_type == false) {
			/* 0x1016101c bit13 select dbi space */
			reg_addr =
				g_pcie_misc_base_virt[pcie_index] + link_ops.ctrl_reg_7[pcie_index];
		}
		/* config pcie2.0 rate */
		force_link_mode(reg_addr, dbi_base_addr + PCIE_2D0_LINK_MODE_REG_OFFSET,
			PCIE_FORCE_LINK_20);
		/* check link status */
		reg_addr = g_pcie_misc_base_virt[pcie_index] + link_ops.link_2d0_reg[pcie_index];
		if (is_pcie_link_stable(reg_addr, PCIE_2D0_LINK_STATUS_MASK, PCIE_2D0_LINK_STATUS))
			pr_info("PCIE%d 2.0 Device Link Up.\n", pcie_index);
		else
			pr_info("PCIE%d 2.0 Device Link Down.\n", pcie_index);
	}

	/* himm 0x101000bc=0x40000000,0x101000c0=0x0 */
	writel(PCIE_WORK_MODE_ENABLE,
		g_pcie_misc_base_virt[pcie_index] + link_ops.ctrl_reg_0[pcie_index]);
	/* ensure link up before operation */
	udelay(PCIE_UDELAY_1000);
	writel(0x0, g_pcie_misc_base_virt[pcie_index] + link_ops.ctrl_reg_1[pcie_index]);
	return 0;
}

int32_t pcie_smbus_config(bool enable)
{
	int32_t ret = 0;
	struct src_i2c_dev_info dev_info = { 0 };
	struct i2c_drv_func *i2c_drv_func_hook;

	i2c_drv_func_hook = get_i2c_func_hook();
	if ((i2c_drv_func_hook == NULL) ||
	    (i2c_drv_func_hook->i2c_drv_set_speed_hook == NULL) ||
	    (i2c_drv_func_hook->i2c_drv_set_work_mode_hook == NULL))
		return -EIO;

	/* enable smbus */
	if (enable == true) {
		/* I2C rate less than 100K */
		ret = i2c_drv_func_hook->i2c_drv_set_speed_hook(&dev_info, SRC_I2C_BAUD_RATE_45K);
		if (ret != 0) {
			pr_info("SMBUS CONFIG ERROR: i2c speed\n");
			return -EIO;
		}

		/* config smbus mode */
		ret = i2c_drv_func_hook->i2c_drv_set_work_mode_hook(&dev_info, I2C_WORK_MODE_MBUS);
		if (ret != 0) {
			pr_info("SMBUS CONFIG ERROR: smbus mode\n");
			return -EIO;
		}
	} else {
		/* config I2C mode */
		ret = i2c_drv_func_hook->i2c_drv_set_work_mode_hook(&dev_info,
			I2C_WORK_MODE_MASTER);
		if (ret != 0) {
			pr_info("SMBUS CONFIG ERROR: i2c mode\n");
			return -EIO;
		}
	}

	return 0;
}

int32_t pcie_smbus_read(uint8_t port, uint8_t reg, uint32_t reg_len,
	uint8_t *data, uint32_t data_len)
{
	uint32_t reg_addr;
	uint32_t ret;
	uint16_t dev_addr = PCIE_SWITCH_PI7C9X2G304SL_DEV_ADDR;
	struct src_i2c_dev_info dev_info = { 0 };
	struct i2c_drv_func *i2c_drv_func_hook = get_i2c_func_hook();

	/* uc_reg_addr for register addr (value is 0x8 + reg offset + port,
	 * such as offset 0x2c, it will be 0x2c08)
	 */
	reg_addr = PCIE_REG_SMBUS_TO_I2C(port, reg);
	*data = PCIE_SMBUS_DATA_INIT_VALUE;
	pr_info("SMBUS CONFIG\n");
	pr_info(
		"dev_addr = 0x%x reg_addr = 0x%x reg_len = 0x%x data = 0x%x data_len = 0x%x\n",
		dev_addr, reg_addr, reg_len, *data, data_len
	);

	dev_info.rw_mode = I2C_RW_MODE_EXTERN; /* #define I2C_RW_MODE_EXTERN  1 */
	if ((i2c_drv_func_hook == NULL) || (i2c_drv_func_hook->i2c_drv_read_data_hook == NULL))
		return -EIO;

	ret = i2c_drv_func_hook->i2c_drv_read_data_hook(&dev_info,
		dev_addr, reg_addr, reg_len, data, data_len);
	if (ret != 0) {
		pr_info("SMBUS READ ERROR: smbus read fail, ");
		pr_info("port=%d, reg=%d, reg_len=%d, data_len=%d\n",
			port, reg, reg_len, data_len);
		return -EIO;
	}

	return 0;
}

int32_t pcie_smbus_write(uint8_t port, uint8_t reg,
	uint32_t reg_len, uint8_t *data, uint32_t data_len)
{
	uint32_t reg_addr;
	uint32_t ret;
	uint16_t dev_addr = PCIE_SWITCH_PI7C9X2G304SL_DEV_ADDR;
	struct src_i2c_dev_info dev_info = { 0 };
	struct i2c_drv_func *i2c_drv_func_hook = get_i2c_func_hook();

	/* uc_reg_addr for register addr (value is 0x8 + reg offset + port
	 * such as offset 0x2c, it will be 0x2c08)
	 */
	reg_addr = PCIE_REG_SMBUS_TO_I2C(port, reg);
	dev_info.rw_mode = I2C_RW_MODE_EXTERN; /* #define I2C_RW_MODE_EXTERN  1 */
	if ((i2c_drv_func_hook == NULL) ||
	    (i2c_drv_func_hook->i2c_drv_write_data_hook == NULL))
		return -EIO;

	ret = i2c_drv_func_hook->i2c_drv_write_data_hook(&dev_info,
		dev_addr, reg_addr, reg_len, data, data_len);
	if (ret != 0) {
		pr_info("SMBUS WRITE ERROR: smbus read fail, ");
		pr_info("port=%d, reg=%d, reg_len=%d, data_len=%d\n",
			port, reg, reg_len, data_len);
		return -EIO;
	}
	return 0;
}

#endif

enum HW_CHIP_ID_E pcie_get_chip_id(void)
{
	if (g_pcie_chip_id == HW_CHIP_ID_NONE_E)
		g_pcie_chip_id = hw_kernel_get_chip_id();

	return g_pcie_chip_id;
}

void pcie_undo_ioremap(void)
{
	/* init failed and release resources */
	uint32_t index;

	for (index = PCIE_INDEX_0; index < PCIE_INDEX_MAX; index++) {
		if (!g_pcie_linkdown_state[index])
			continue;

		if (g_pcie_ctrl_info[index].misc_base != NULL) {
			iounmap(g_pcie_ctrl_info[index].misc_base);
			g_pcie_ctrl_info[index].misc_base = NULL;
		}
		pcie_clk_poweroff(index);
	}
}

void pcie_clk_power_exit(void)
{
	uint32_t index;

	for (index = PCIE_INDEX_0; index < PCIE_INDEX_MAX; index++)
		if (!g_pcie_linkdown_state[index])
			pcie_clk_poweroff(index);
}

uint32_t pcie_loopback_test(void *pcie_test_data)
{
	uint32_t ret = 0;
	uint32_t loop;
	struct dev_pcie_test_s *pst_pcie_test_data = NULL;

	pst_pcie_test_data = (struct dev_pcie_test_s *)pcie_test_data;
	if ((pst_pcie_test_data->ui_pcie_id > PCIE_INDEX_2) ||
	    (pst_pcie_test_data->ui_pcie_size > PCIE_TEST_DATA_HALF_LEN) ||
	    (pst_pcie_test_data->ui_pcie_opt > PCIE_TEST_OPT_RESULT)) {
		return -EINVAL;
	}

	if ((pst_pcie_test_data->ui_pcie_opt == PCIE_TEST_OPT_ENABLE) ||
	    (pst_pcie_test_data->ui_pcie_opt == PCIE_TEST_OPT_DISABLE)) {
		ret = pcie_loopback_set(pst_pcie_test_data->ui_pcie_id,
			pst_pcie_test_data->ui_pcie_opt, pst_pcie_test_data->chip_id);
	}

	if (pst_pcie_test_data->ui_pcie_opt == PCIE_TEST_OPT_START) {
		if (pst_pcie_test_data->ui_pcie_times < 1)
			return -EINVAL;

		for (loop = 0; loop < pst_pcie_test_data->ui_pcie_times; loop++) {
			ret = pcie_loopback_start(pst_pcie_test_data->ui_pcie_id,
				pst_pcie_test_data->ui_pcie_size, pst_pcie_test_data->chip_id);
			if (ret != 0)
				return -EIO;
		}
	}
	return ret;
}

void pcie_set_iatu_table(struct pcie_iatu *pcie0_iatu,
	struct pcie_iatu *pcie1_iatu, struct pcie_iatu *pcie2_iatu)
{
	g_pcie0_iatu_table = pcie0_iatu;
	g_pcie1_iatu_table = pcie1_iatu;
	g_pcie2_iatu_table = pcie2_iatu;
}

void pcie_set_iatu_table_len(uint32_t pcie0_len, uint32_t pcie1_len, uint32_t pcie2_len)
{
	g_pcie0_iatu_table_len = pcie0_len;
	g_pcie1_iatu_table_len = pcie1_len;
	g_pcie2_iatu_table_len = pcie2_len;
}

void pcie_get_misc_base_virt(uint32_t index, void **__iomem pcie_misc_base)
{
	*pcie_misc_base = g_pcie_misc_base_virt[index];
}

void pcie_set_misc_base_virt(uint32_t index, void *__iomem pcie_misc_base)
{
	g_pcie_misc_base_virt[index] = pcie_misc_base;
	g_pcie_ctrl_info[index].misc_base = pcie_misc_base;
}

void pcie_get_ctrl_info(struct pcie_info **pcie_ctrl)
{
	*pcie_ctrl = g_pcie_ctrl_info;
}

static void do_1ms_delay(void *data)
{
	mdelay(1);
}

void disable_l2cache_outer(void)
{
	unsigned long flags = 0;

	/* do some delay on all other cpus. */
	smp_call_function(do_1ms_delay, NULL, 0);

	/* disable outer cache on local cpu. */
	local_irq_save(flags);
	preempt_disable();

	outer_cache.inv_range = NULL;
	outer_cache.clean_range = NULL;
	outer_cache.flush_range = NULL;
	outer_cache.flush_all = NULL;
#if defined(CONFIG_HISI_L2_OUTER_CACHE) || defined(CONFIG_HISI_L3_OUTER_CACHE)
	outer_cache.clean_all = NULL;
	outer_cache.inv_all = NULL;
#endif
#ifdef CONFIG_OUTER_CACHE_SYNC
	outer_cache.sync = NULL;
#endif
	local_irq_restore(flags);
	preempt_enable();

	pr_info("disable outer cache ops...\n");
}

void pcie_bridge_config_pcie10(void)
{
	int32_t ret;
	/* set downlink of pcie switch to pcie1.0, port number 2 */
	int32_t portid = PCIE_SWITCH_DOWN_PORT;
	uint32_t loop;
	uint16_t reg_value = 0;
	uint32_t switch_reg_len = PCIE_SWITCH_PI7C9X2G304SL_REG_LEN;

	/* init as smbus mode */
	ret = pcie_smbus_config(true);
	if (ret != 0) {
		pr_info("SMBUS INIT ERROR: smbus enable\n");
		return;
	}

	/* clear bus */
	for (loop = 0; loop < PCIE_SWITCH_READ_TRY; loop++) {
		ret = pcie_smbus_read(0, 0, switch_reg_len,
			(uint8_t *)&reg_value, PCIE_SMBUS_DATA_LEN);
		if (ret == 0)
			break;
	}

	/* 0x8a bit 15, set 1 for pcie 1.0 mode */
	ret |= pcie_smbus_read(portid, PCIE_1D0_CAPABILITY_REG,
		switch_reg_len, (uint8_t *)&reg_value, PCIE_SMBUS_DATA_LEN);
	reg_value |= (1 << PCIE_BIT_15);
	ret |= pcie_smbus_write(portid, PCIE_1D0_CAPABILITY_REG, switch_reg_len, NULL, 0);
	ret |= pcie_smbus_write(portid, PCIE_1D0_CMD_REG, PCIE_SMBUS_REG_ADDR_LEN,
		(uint8_t *)&reg_value, PCIE_SMBUS_DATA_LEN);

	/* 0xD0 bit 4, set 1, then set 0 to enable down/up interface cfg */
	ret |= pcie_smbus_read(portid, PCIE_1D0_CFG_ENABLE_REG, switch_reg_len,
		(uint8_t *)&reg_value, PCIE_SMBUS_DATA_LEN);
	reg_value |= (1 << PCIE_BIT_4);
	ret |= pcie_smbus_write(portid, PCIE_1D0_CFG_ENABLE_REG, switch_reg_len, NULL, 0);
	ret |= pcie_smbus_write(portid, PCIE_1D0_CMD_REG, PCIE_SMBUS_REG_ADDR_LEN,
		(uint8_t *)&reg_value, PCIE_SMBUS_DATA_LEN);

	/* enable up interface cfg */
	ret |= pcie_smbus_read(portid, PCIE_1D0_CFG_ENABLE_REG, switch_reg_len,
		(uint8_t *)&reg_value, PCIE_SMBUS_DATA_LEN);
	reg_value &= (~(1 << PCIE_BIT_4));
	ret |= pcie_smbus_write(portid, PCIE_1D0_CFG_ENABLE_REG, switch_reg_len, NULL, 0);
	ret |= pcie_smbus_write(portid, PCIE_1D0_CMD_REG, PCIE_SMBUS_REG_ADDR_LEN,
		(uint8_t *)&reg_value, PCIE_SMBUS_DATA_LEN);

	/* restore to I2C mode */
	ret |= pcie_smbus_config(false);
	if (ret != 0)
		pr_info("SMBUS INIT ERROR: smbus disable\n");
}

void hi_pcie_post_config(uint32_t port)
{
	uint32_t i;
	struct pcie_reg_op *reg_op_list;

	if (port >= PCIE_INDEX_MAX)
		return;

	/* no cfg and no need to config */
	if ((g_pcie_reg_base_addr[port].reg_op_list_num == 0) ||
	    (g_pcie_reg_base_addr[port].reg_op_list == NULL))
		return;
	reg_op_list = g_pcie_reg_base_addr[port].reg_op_list;

	for (i = 0; i < g_pcie_reg_base_addr[port].reg_op_list_num; i++) {
		int32_t ret = clear_reg_iomap_mask_set_val(reg_op_list[i].addr,
			reg_op_list[i].mask, reg_op_list[i].val);
		if (ret != 0)
			return;
	}
}

void pcie_enable_clk(uint32_t pcie_id)
{
	uint32_t stnum;
	uint32_t loop;
	uint32_t value = 0;
	uint32_t old_value = 0;
	int32_t ret;
	struct pcie_clk_reg pcie_clk_info[] = {
		{
			HW_CHIP_ID_5116TV5_E,
			{{ PCIE_CLK_EN_REG2, PCIE_CLK_EN_BIT13_MASK },
				{ PCIE_CLK_EN_REG2, PCIE_CLK_EN_BIT14_MASK }},
			{{ 0, 0 }, { 0, 0 }}
		}, {
			HW_CHIP_ID_5117P_E,
			{{ PCIE_CLK_EN_REG, PCIE_CLK_EN_BIT13_MASK },
				{ PCIE_CLK_EN_REG, PCIE_CLK_EN_BIT14_MASK }},
			{{ PCIE_REF_CLK_EN_REG, PCIE0_REF_CLK_EN_MASK },
				{ PCIE_REF_CLK_EN_REG, PCIE1_REF_CLK_EN_MASK }}
		}, {
			HW_CHIP_ID_5117V_E,
			{{ PCIE_CLK_EN_REG, PCIE_CLK_EN_BIT13_MASK },
				{ PCIE_CLK_EN_REG, PCIE_CLK_EN_BIT14_MASK }},
			{{ PCIE_REF_CLK_EN_REG, PCIE0_REF_CLK_EN_MASK },
			{ PCIE_REF_CLK_EN_REG, PCIE1_REF_CLK_EN_MASK }}
		}, {
			HW_CHIP_ID_5117L_E,
			{{ PCIE_CLK_EN_REG2, PCIE_CLK_EN_BIT13_MASK },
				{ PCIE_CLK_EN_REG2, PCIE_CLK_EN_BIT15_MASK }},
			{{ PCIE_REF_CLK_EN_REG, PCIE0_REF_CLK_EN_MASK },
				{ PCIE_REF_CLK_EN_REG, PCIE1_REF_CLK_EN_MASK }}
		}, {
			HW_CHIP_ID_5182H_E,
			{{ PCIE_CLK_EN_REG2, PCIE_CLK_EN_BIT12_MASK },
				{ PCIE_CLK_EN_REG2, PCIE_CLK_EN_BIT13_MASK }},
			{{ PCIE_REF_CLK_EN_REG, PCIE0_REF_CLK_EN_MASK },
				{ PCIE_REF_CLK_EN_REG, PCIE1_5182H_REF_CLK_MASK }}
		}, {
			HAL_CHIP_ID_5117PV5,
			{{ PCIE_CLK_EN_REG, PCIE_CLK_EN_BIT13_MASK },
				{ PCIE_CLK_EN_REG, PCIE_CLK_EN_BIT14_MASK }},
			{{ PCIE_REF_CLK_EN_REG, PCIE0_REF_CLK_EN_MASK },
				{ PCIE_REF_CLK_EN_REG, PCIE1_REF_CLK_EN_MASK }}
		}, {
			HAL_CHIP_ID_5182HV5,
			{{ PCIE_CLK_EN_REG2, PCIE_CLK_EN_BIT12_MASK },
				{ PCIE_CLK_EN_REG2, PCIE_CLK_EN_BIT13_MASK }},
			{{ PCIE_REF_CLK_EN_REG, PCIE0_REF_CLK_EN_MASK },
				{ PCIE_REF_CLK_EN_REG, PCIE1_5182H_REF_CLK_MASK }}
		}, {
			HAL_CHIP_ID_5182T,
			{{ PCIE_CLK_EN_REG2, PCIE0_CLK_EN_MASK_5182T },
				{ PCIE_CLK_EN_REG2, PCIE1_CLK_EN_MASK_5182T },
				{ PCIE_CLK_EN_REG2, PCIE2_CLK_EN_MASK_5182T }},
			{{ PCIE_REF_CLK_EN_REG, PCIE0_REF_CLK_EN_MASK_5182T },
				{ PCIE_REF_CLK_EN_REG, PCIE1_REF_CLK_EN_MASK_5182T },
				{ PCIE_REF_CLK_EN_REG, PCIE2_REF_CLK_EN_MASK_5182T }}
		}, {
			HAL_CHIP_ID_1156H,
			{{ PCIE_CTRL_CLK_1156_REG, PCIE_CTRL_CLK_1156_MSK },
				{ 0, 0 }, { 0, 0 }},
			{{ PCIE_CTRL_CLK_PHY_1156_REG, PCIE_CTRL_CLK_PHY_1156_MSK },
				{ 0, 0 }, { 0, 0 }}
		}, {
			HAL_CHIP_ID_1155H,
			{{ PCIE_CTRL_CLK_1156_REG, PCIE_CTRL_CLK_1156_MSK },
				{ 0, 0 }, { 0, 0 }},
			{{ PCIE_CTRL_CLK_PHY_1156_REG, PCIE_CTRL_CLK_PHY_1156_MSK },
				{ 0, 0 }, { 0, 0 }}
		}, {
			HAL_CHIP_ID_1156E,
			{{ PCIE_CTRL_CLK_1156_REG, PCIE_CTRL_CLK_1156_MSK },
			{ 0, 0 }, { 0, 0 }},
			{{ PCIE_CTRL_CLK_PHY_1156_REG, PCIE_CTRL_CLK_PHY_1156_MSK },
			{ 0, 0 }, { 0, 0 }}
		}, {
			HAL_CHIP_ID_2952H,
			{{ PCIE_CTRL_CLK_1156_REG, PCIE_CTRL_CLK_1156_MSK },
			{ 0, 0 }, { 0, 0 }},
			{{ PCIE_CTRL_CLK_PHY_1156_REG, PCIE_CTRL_CLK_PHY_1156_MSK },
			{ 0, 0 }, { 0, 0 }}
		},
	};

	/* start pcie0 or pcie1 */
	stnum = sizeof(pcie_clk_info) / sizeof(struct pcie_clk_reg);
	for (loop = 0; loop < stnum; loop++) {
		if (g_pcie_chip_id == pcie_clk_info[loop].chip_id) {
			/* pcie clk cfg */
			ret = read_reg_iomap_value(pcie_clk_info[loop].clk_cfg[pcie_id].reg,
				&old_value);
			value = old_value;
			value |= pcie_clk_info[loop].clk_cfg[pcie_id].value;
			ret |= write_reg_iomap_value(pcie_clk_info[loop].clk_cfg[pcie_id].reg,
				value);
			pr_info("pcie%u clk:%#x >> %#x\n", pcie_id, old_value, value);

			/* pcie reference clk cfg */
			ret |= read_reg_iomap_value(pcie_clk_info[loop].ref_clk[pcie_id].reg,
				&old_value);
			value = old_value;
			value &= ~(pcie_clk_info[loop].ref_clk[pcie_id].value);
			ret |= write_reg_iomap_value(pcie_clk_info[loop].ref_clk[pcie_id].reg,
				value);
			pr_info("pcie%u clk:%#x >> %#x\n", pcie_id, old_value, value);
			if (ret != 0)
				pr_info("pcie%d clk enable fail!\n", pcie_id);

			return;
		}
	}
	/* chip not found means saving energy is not required, no error report */
}

void pcie_clk_poweroff(uint32_t pcie_id)
{
	uint32_t stnum;
	uint32_t loop;
	uint32_t value = 0;
	int32_t ret = 0;
	struct pcie_clk_reg pcie_clk_info[] = {
		{
			HW_CHIP_ID_5116TV5_E,
			{{ PCIE_CLK_EN_REG2, PCIE_CLK_EN_BIT13_MASK },
				{ PCIE_CLK_EN_REG2, PCIE_CLK_EN_BIT14_MASK }, { 0, 0 }},
			{{ 0, 0 }, { 0, 0 }, { 0, 0 }}
		}, {
			HW_CHIP_ID_5117P_E,
			{{ PCIE_CLK_DIS_REG, PCIE_CLK_EN_BIT13_MASK },
				{ PCIE_CLK_DIS_REG, PCIE_CLK_EN_BIT14_MASK }, { 0, 0 }},
			{{ PCIE_REF_CLK_EN_REG, PCIE0_REF_CLK_EN_MASK },
				{ PCIE_REF_CLK_EN_REG, PCIE1_REF_CLK_EN_MASK }, { 0, 0 }}
		}, {
			HW_CHIP_ID_5117V_E,
			{{ PCIE_CLK_DIS_REG, PCIE_CLK_EN_BIT13_MASK },
				{ PCIE_CLK_DIS_REG, PCIE_CLK_EN_BIT14_MASK }, { 0, 0 }},
			{{ PCIE_REF_CLK_EN_REG, PCIE0_REF_CLK_EN_MASK },
				{ PCIE_REF_CLK_EN_REG, PCIE1_REF_CLK_EN_MASK }, { 0, 0 }}
		}, {
			HW_CHIP_ID_5117L_E,
			{{ PCIE_CLK_DIS_REG2, PCIE_CLK_EN_BIT13_MASK },
				{ PCIE_CLK_DIS_REG2, PCIE_CLK_EN_BIT15_MASK }, { 0, 0 }},
			{{ PCIE_REF_CLK_EN_REG, PCIE0_REF_CLK_EN_MASK },
				{ PCIE_REF_CLK_EN_REG, PCIE1_REF_CLK_EN_MASK }, { 0, 0 }}
		}, {
			HW_CHIP_ID_5182H_E,
			{{ PCIE_CLK_DIS_REG2, PCIE_CLK_EN_BIT12_MASK },
				{ PCIE_CLK_DIS_REG2, PCIE_CLK_EN_BIT13_MASK }, { 0, 0 }},
			{{ PCIE_REF_CLK_EN_REG, PCIE0_REF_CLK_EN_MASK },
				{ PCIE_REF_CLK_EN_REG, PCIE1_5182H_REF_CLK_MASK }, { 0, 0 }}
		}, {
			HAL_CHIP_ID_5117PV5,
			{{ PCIE_CLK_DIS_REG, PCIE_CLK_EN_BIT13_MASK },
				{ PCIE_CLK_DIS_REG, PCIE_CLK_EN_BIT14_MASK }, { 0, 0 }},
			{{ PCIE_REF_CLK_EN_REG, PCIE0_REF_CLK_EN_MASK },
				{ PCIE_REF_CLK_EN_REG, PCIE1_REF_CLK_EN_MASK }, { 0, 0 }}
		}, {
			HAL_CHIP_ID_5182HV5,
			{{ PCIE_CLK_DIS_REG2, PCIE_CLK_EN_BIT12_MASK },
				{ PCIE_CLK_DIS_REG2, PCIE_CLK_EN_BIT13_MASK }, { 0, 0 }},
			{{ PCIE_REF_CLK_EN_REG, PCIE0_REF_CLK_EN_MASK },
				{ PCIE_REF_CLK_EN_REG, PCIE1_5182H_REF_CLK_MASK }, { 0, 0 }}
		}, {
			HAL_CHIP_ID_5182T,
			{{ PCIE_CLK_DIS_REG2, PCIE0_CLK_EN_MASK_5182T },
				{ PCIE_CLK_DIS_REG2, PCIE1_CLK_EN_MASK_5182T },
				{ PCIE_CLK_DIS_REG2, PCIE2_CLK_EN_MASK_5182T }},
			{{ PCIE_REF_CLK_EN_REG, PCIE0_REF_CLK_EN_MASK_5182T },
				{ PCIE_REF_CLK_EN_REG, PCIE1_REF_CLK_EN_MASK_5182T },
				{ PCIE_REF_CLK_EN_REG, PCIE2_REF_CLK_EN_MASK_5182T }}
		}, {
			HAL_CHIP_ID_1156H,
			{{ PCIE_CTRL_CLK_1156_REG, 0 }, { 0, 0 }, { 0, 0 }},
			{{ PCIE_CTRL_CLK_PHY_1156_REG, 0 }, { 0, 0 }, { 0, 0 }}
		}, {
			HAL_CHIP_ID_1155H,
			{{ PCIE_CTRL_CLK_1156_REG, 0 }, { 0, 0 }, { 0, 0 }},
			{{ PCIE_CTRL_CLK_PHY_1156_REG, 0 }, { 0, 0 }, { 0, 0 }}
		},
	};

	/* close pcie0 or pcie1 */
	stnum = sizeof(pcie_clk_info) / sizeof(struct pcie_clk_reg);
	for (loop = 0; loop < stnum; loop++) {
		if (g_pcie_chip_id == pcie_clk_info[loop].chip_id) {
			/* pcie disable clk cfg */
			if (pcie_clk_info[loop].clk_cfg[pcie_id].reg != 0) {
				ret = read_reg_iomap_value(
					pcie_clk_info[loop].clk_cfg[pcie_id].reg, &value
				);
				value |= pcie_clk_info[loop].clk_cfg[pcie_id].value;
				ret |= write_reg_iomap_value(
					pcie_clk_info[loop].clk_cfg[pcie_id].reg, value
				);
			}

			/* pcie disable reference clk cfg */
			if (pcie_clk_info[loop].ref_clk[pcie_id].reg != 0) {
				ret |= read_reg_iomap_value(
					pcie_clk_info[loop].ref_clk[pcie_id].reg, &value
				);
				value |= pcie_clk_info[loop].ref_clk[pcie_id].value;
				ret |= write_reg_iomap_value(
					pcie_clk_info[loop].ref_clk[pcie_id].reg, value
				);
			}

			if (ret != 0)
				pr_info("pcie%d clk disable fail!\n", pcie_id);

			pr_info("pcie%d clk poweroff\n", pcie_id);
			return;
		}
	}
}

int32_t pcie_ctrl_init(uint32_t pcie_index,
	uint32_t cfg_size, uint32_t mem_size, uint32_t *controllers_nr)
{
	uint32_t val;
	uint32_t ctrl_num;
	enum pcie_ctrl_type controller_array[PCIE_INDEX_MAX] = {
		PCIE_CTRL_TYPE_0, PCIE_CTRL_TYPE_1, PCIE_CTRL_TYPE_2 };
	uint32_t cfg_base_addr[PCIE_INDEX_MAX] = {
		g_pcie_reg_base_addr[PCIE_INDEX_0].cfg_base_addr,
		g_pcie_reg_base_addr[PCIE_INDEX_1].cfg_base_addr,
		g_pcie_reg_base_addr[PCIE_INDEX_2].cfg_base_addr };
	uint32_t mem_base_addr[PCIE_INDEX_MAX] = {
		g_pcie_reg_base_addr[PCIE_INDEX_0].mem_base_addr,
		g_pcie_reg_base_addr[PCIE_INDEX_1].mem_base_addr,
		g_pcie_reg_base_addr[PCIE_INDEX_2].mem_base_addr };
	uint32_t pcie_peri_offset[PCIE_INDEX_MAX] = { PERI_PCIE0_5115, PERI_PCIE1_0 };

	if (((pcie_index == PCIE_INDEX_0) && (g_pcie_linkdown_state[PCIE_INDEX_0])) ||
	    ((pcie_index == PCIE_INDEX_1) && (g_pcie_linkdown_state[PCIE_INDEX_1])) ||
	    ((pcie_index == PCIE_INDEX_2) && (g_pcie_linkdown_state[PCIE_INDEX_2])))
		/* if no link, return directly to continue starting */
		return 0;

	ctrl_num = *controllers_nr;
	if (ctrl_num >= PCIE_INDEX_MAX) {
		pr_info("Pcie controller is not enough %d!\n", ctrl_num);
		return -EIO;
	}

	/* init pcie work mode in advance */
	g_pcie_ctrl_info[ctrl_num].work_mode = PCIE_WM_RC;
	g_pcie_ctrl_info[ctrl_num].root_bus_nr = -1;
	g_pcie_ctrl_info[ctrl_num].controller = controller_array[pcie_index];
	g_pcie_ctrl_info[ctrl_num].conf_base_addr =
		(uint32_t)ioremap(cfg_base_addr[pcie_index], cfg_size);
	if (!g_pcie_ctrl_info[ctrl_num].conf_base_addr) {
		pr_info("Cannot map host's dbi space for controller %d!\n", pcie_index);
		return -EIO;
	}

	g_pcie_mem_base_addr[ctrl_num] = ioremap(mem_base_addr[pcie_index], mem_size);
	g_pcie_ctrl_info[ctrl_num].base_addr = (uint32_t)g_pcie_mem_base_addr[ctrl_num];
	if (!g_pcie_ctrl_info[ctrl_num].base_addr) {
		pr_info("Cannot map config space base for controller %d!\n", pcie_index);
		return -EIO;
	}

	/* only start controller after linkup and space both init */
	*controllers_nr = ctrl_num + 1;
	if (is_old_soc_chip()) {
		/* work at ep mode This is roughly initializtion of ep mode. */
		val = readl(g_pcie_misc_base_virt[pcie_index] + pcie_peri_offset[pcie_index]);
		val &= (~(PCIE_SLV_DEVICE_TYPE_MASK << PCIE_SLV_DEVICE_TYPE));
		writel(val, g_pcie_misc_base_virt[pcie_index] + pcie_peri_offset[pcie_index]);
	}
	return HW_RET_SUCCESS;
}

/* get where_ offset after 4 bit addr alignment */
#define PCIE_WHERE_OFFSET(addr_)   (((uintptr_t)(addr_) & 0x3) << 3)
#define PCIE_ADDR_ALIGN4(addr_)    ((void *)((uintptr_t)(addr_) & ~3))

#define PCIE_PLR_BASE  0x700   /* Port Logic Registers Base */
#define PCIE_PLR_GEN2_CTRL  (PCIE_PLR_BASE + 0x10C)
#define PCIE_PLR_GEN2_CTRL_LANE_NUM_POS  8
#define PCIE_PLR_GEN2_CTRL_LANE_NUM_MSK  (0x1FFUL << PCIE_PLR_GEN2_CTRL_LANE_NUM_POS)
#define PCIE_PLR_PORT_LINK_CTRL  (PCIE_PLR_BASE + 0x10)
#define PCIE_PLR_PORT_LINK_CTRL_LINE_MODE_EN_POS 16
#define PCIE_PLR_PORT_LINK_CTRL_LINE_MODE_EN_MSK \
	(0x3FUL << PCIE_PLR_PORT_LINK_CTRL_LINE_MODE_EN_POS)
#define PCIE_PLR_PORT_LINK_CTRL_LOOPBACK_EN_POS  2
#define PCIE_PLR_PORT_LINK_CTRL_LOOPBACK_EN_MSK \
	(0x1UL << PCIE_PLR_PORT_LINK_CTRL_LOOPBACK_EN_POS)

int32_t pcie_readl_dbi(void __iomem *addr, uint32_t size, uint32_t *val)
{
	uint32_t value, offset;

	if ((uintptr_t)addr & (size - 1))
		return -EIO;

	value = readl(PCIE_ADDR_ALIGN4(addr));
	offset = PCIE_WHERE_OFFSET(addr);
	if (size == 4)
		*val = value;
	else if (size == 2)
		*val = (value >> offset) & 0xFFFF;
	else if (size == 1)
		*val = (value >> offset) & 0xFF;
	else
		return -EIO;

	return 0;
}

int32_t pcie_writel_dbi(void __iomem *addr, uint32_t size, uint32_t val)
{
	uint32_t value, offset;

	if ((uintptr_t)addr & (size - 1))
		return -EIO;

	value = readl(PCIE_ADDR_ALIGN4(addr));
	offset = PCIE_WHERE_OFFSET(addr);
	if (size == 4) {
		value = val;
	} else if (size == 2) {
		value &= ~(0xFFFF << offset);
		value |= val << offset;
	} else if (size == 1) {
		value &= ~(0xFF << offset);
		value |= val << offset;
	} else {
		return -EIO;
	}

	writel(value, PCIE_ADDR_ALIGN4(addr));
	return 0;
}

/*
 * pcie lane num set, when 5182T PCIE3.0 at dual x1 mode,
 * need to config PCIE0 and PCIE lane number as 1
 */
void pcie_lane_num_set(uint32_t pcie_index, uint32_t lane_num)
{
	uint32_t val = 0;

	if (lane_num == 0)
		return;

	(void)pcie_readl_dbi(g_pcie_ctrl_info[pcie_index].dbi_base + PCIE_PLR_PORT_LINK_CTRL,
		sizeof(val), &val);
	val &= ~PCIE_PLR_PORT_LINK_CTRL_LINE_MODE_EN_MSK;
	val |= ((lane_num * 2 - 1) << PCIE_PLR_PORT_LINK_CTRL_LINE_MODE_EN_POS);
	(void)pcie_writel_dbi(g_pcie_ctrl_info[pcie_index].dbi_base + PCIE_PLR_PORT_LINK_CTRL,
		sizeof(val), val);

	(void)pcie_readl_dbi(g_pcie_ctrl_info[pcie_index].dbi_base + PCIE_PLR_GEN2_CTRL,
		sizeof(val), &val);
	val &= ~PCIE_PLR_GEN2_CTRL_LANE_NUM_MSK;
	val |= (lane_num << PCIE_PLR_GEN2_CTRL_LANE_NUM_POS);
	(void)pcie_writel_dbi(g_pcie_ctrl_info[pcie_index].dbi_base + PCIE_PLR_GEN2_CTRL,
		sizeof(val), val);
}

void pcie_dev_dfx_record(void)
{
	struct pcie_dfx_func *pcie_dfx_record_hook = get_pcie_dfx_record_func_hook();

	if ((pcie_dfx_record_hook == NULL) || (pcie_dfx_record_hook->pcie_dfx_record == NULL))
		return;

	pcie_dfx_record_hook->pcie_dfx_record();
}

uint32_t pcie_hisi_sys_init(uint32_t pcie_index, struct pcie_link_ops_reg link_ops)
{
	void *__iomem reg_addr = NULL;
	void *__iomem dbi_base = NULL;
	bool is_force_1d1 = false;
	uint32_t ret = 0;

	/* disable controller first,himm 0x101000d8=0x0 */
	writel(0x0, g_pcie_misc_base_virt[pcie_index] + link_ops.ctrl_reg_7[pcie_index]);
	/* himm 0x101000c0=0x200000,0x101000d4=0x8000000 */
	if ((g_pcie_chip_id != HAL_CHIP_ID_5182T) && (g_pcie_chip_id != HAL_CHIP_ID_1156H) &&
		(g_pcie_chip_id != HAL_CHIP_ID_1155H) && (g_pcie_chip_id != HAL_CHIP_ID_1156E) &&
		(g_pcie_chip_id != HAL_CHIP_ID_2952H)) {
		writel(PCIE_DIS_CTRL_VALUE1,
			g_pcie_misc_base_virt[pcie_index] + link_ops.ctrl_reg_1[pcie_index]);
	}
	writel(PCIE_DIS_CTRL_VALUE6,
		g_pcie_misc_base_virt[pcie_index] + link_ops.ctrl_reg_6[pcie_index]);

	/* setup phy,himm 0x101000dc=0x5605001,0x101000e0=0x20050006 */
	if (link_ops.is_5115_type == true) {
		writel(PCIE_PHY_VALUE0,
			g_pcie_misc_base_virt[pcie_index] + link_ops.ctrl_reg_8[pcie_index]);
		writel(PCIE_PHY_VALUE1,
			g_pcie_misc_base_virt[pcie_index] + link_ops.ctrl_reg_9[pcie_index]);
	}

	/* set work mode,himm 0x101000bc=0x40200000 */
	writel(PCIE_WORK_MODE_VALUE,
		g_pcie_misc_base_virt[pcie_index] + link_ops.ctrl_reg_0[pcie_index]);
	if (link_ops.is_5115_type == false) {
		/* visit DBI space, 0x1016101c bit13 select DBI space */
		set_reg_bit(g_pcie_misc_base_virt[pcie_index] +
			link_ops.ctrl_reg_7[pcie_index], PCIE_BIT_13);
	}
	if (g_pcie_ctrl_info[pcie_index].dbi_base == NULL) {
		/* dbi space addr mapping */
		dbi_base = ioremap(link_ops.dbi_base_addr[pcie_index], DBI_SPACE_SIZE);
		if (dbi_base == NULL) {
			pr_info("Cannot map dbi base for pcie%d\n", pcie_index);
			return -EIO;
		}
		g_pcie_ctrl_info[pcie_index].dbi_base = dbi_base;
	}
	writel(DBI_SPACE_ENABLE, g_pcie_ctrl_info[pcie_index].dbi_base + DBI_CMD_STATUS_OFFSET);

	/* 5182T special process: iatu table, lane num, forced PCIE2.0 rate */
	if ((g_pcie_chip_id == HAL_CHIP_ID_5182T) || (g_pcie_chip_id == HAL_CHIP_ID_1156H) ||
		(g_pcie_chip_id == HAL_CHIP_ID_1155H) || (g_pcie_chip_id == HAL_CHIP_ID_1156E) ||
		(g_pcie_chip_id == HAL_CHIP_ID_2952H)) {
		int32_t ret;
		uint32_t val = 0;

		void *__iomem iatu_cfg_base =
			ioremap(link_ops.dbi_base_addr[pcie_index] + PCIE_IATU_CFG_BASE_5182T,
				DBI_SPACE_SIZE);
		if (iatu_cfg_base == NULL) {
			pr_info("Cannot map iatu cfg base for pcie%d\n", pcie_index);
			return -EIO;
		}

		/* setup iatu table */
		download_iatu_table(pcie_index, iatu_cfg_base);
		iounmap(iatu_cfg_base);

		ret = read_reg_iomap_value(PCIE3_MODE_REG_5182T, &val);
		if (ret != HW_RET_SUCCESS)
			return -EIO;

		/* when PCIE3 config as dual pcie_x1 mode,
		 * need to config PCIE0 and PCIE lane number as 1
		 */
		if ((val == PCIE_DOUBLE_X1_MODE) || (val == PCIE_X1_XFI_MODE))
			if (pcie_index <= PCIE_INDEX_1)
				pcie_lane_num_set(pcie_index, 1);

		if (pcie_is_force_link_mode_2D0(pcie_index))
			is_force_1d1 = true;
	} else {
		/* setup iatu table */
		download_iatu_table(pcie_index, g_pcie_ctrl_info[pcie_index].dbi_base);
	}

	if (link_ops.is_5115_type == false) {
		/* clear 0x1016101c bit13 select misc space */
		clear_reg_bit(g_pcie_misc_base_virt[pcie_index] +
			link_ops.ctrl_reg_7[pcie_index],
			PCIE_BIT_13);
	}

	/* pcie version cfg (actually rate control),
	 * must before LTSSM ENABLE. sd5117L using pcie 1.1
	 * and no pcie2.0
	 */
	if (is_force_1d1 || (g_pcie_chip_id == HW_CHIP_ID_5117L_E) ||
	    (pcie_is_force_link_mode(pcie_index))) {
		reg_addr = g_pcie_misc_base_virt[pcie_index] + link_ops.ctrl_reg_7[pcie_index];
		force_link_mode(reg_addr,
			g_pcie_ctrl_info[pcie_index].dbi_base + PCIE_1D1_LINK_MODE_REG_OFFSET,
			PCIE_FORCE_LINK_11);
	}

	/* enable controller,himm 0x101000d8=0x800 */
	reg_addr = g_pcie_misc_base_virt[pcie_index] + link_ops.ctrl_reg_7[pcie_index];
	if (g_pcie_chip_id == HAL_CHIP_ID_5182T)
		orr_reg_value(reg_addr, (1 << PCIE_APP_LTSSM_ENABLE) | PCIE_EN_CTRL_VALUE7_5182T);
	else
		orr_reg_value(reg_addr, (1 << PCIE_APP_LTSSM_ENABLE) | PCIE_EN_CTRL_VALUE7);

	udelay(PCIE_UDELAY_1000);

	/* workaround for disabling 5118v200 PCIE detect state machine when connected to QCA9800 */
	special_handle_5118V2(g_pcie_misc_base_virt[pcie_index], pcie_index);
	ret = pcie_check_link_mode(pcie_index, link_ops,
		g_pcie_ctrl_info[pcie_index].dbi_base);
	if (ret == EIO)
		pcie_dev_dfx_record();

	return ret;
}

void pcie_dev_force_close(void)
{
	struct pcie_info *pcie_ctrl = NULL;
	uint32_t i = PCIE_INDEX_0;
	get_section_cfg_funcptr *get_section_cfg_func;
	uint32_t cfg_num;
	uint32_t ret;
	uint8_t section[] = "pcie";
	static struct src_ini_section_parse reset_cfg[] = {
		{ "pcie0_reset_gpio", PCIE_INVALID_INT32 },
		{ "pcie1_reset_gpio", PCIE_INVALID_INT32 },
		{ "wifi0_reset_gpio", PCIE_INVALID_INT32 },
		{ "wifi1_reset_gpio", PCIE_INVALID_INT32 },
		{ "wifi_qca_enable_gpio",  PCIE_INVALID_INT32 },
		{ "wifi1_qca_enable_gpio", PCIE_INVALID_INT32 },
		{ "qca_wifi0_reset_gpio",  PCIE_INVALID_INT32 },
		{ "qca_wifi1_reset_gpio",  PCIE_INVALID_INT32 },
		{ "ext_rf0_power_gpio",    PCIE_INVALID_INT32 },
		{ "ext_rf1_power_gpio",    PCIE_INVALID_INT32 },
	};

	pcie_get_ctrl_info(&pcie_ctrl);
	for (; i < PCIE_INDEX_MAX; i++) {
		if (pcie_ctrl[i].misc_base != NULL) {
			writel(0xffffffff, pcie_ctrl[i].misc_base + 0x28);
			writel(0, pcie_ctrl[i].misc_base + 0x2c);
			writel(0xffffffff, pcie_ctrl[i].misc_base + 0x2c);
		}
	}

	get_section_cfg_func = get_section_cfg_func_hook();
	if ((get_section_cfg_func == NULL) || (*get_section_cfg_func == NULL))
		return;

	cfg_num = ARRAY_SIZE(reset_cfg);
	ret = (*get_section_cfg_func)(section, reset_cfg, cfg_num);
	if (ret != HW_RET_SUCCESS) {
		pr_err("not found pcie ini file\n");
		return;
	}

	for (i = 0; i < cfg_num; i++) {
		if (reset_cfg[i].value != PCIE_INVALID_INT32)
			(void)set_gpio_output_value(reset_cfg[i].value, GPIO_IO_LOW);
	}
}

struct pcie_info *get_pcie_info_by_index(uint32_t index)
{
	if (index >= ARRAY_SIZE(g_pcie_ctrl_info))
		return NULL;

	return &g_pcie_ctrl_info[index];
}

bool pcie_get_linkup_status(uint32_t pcie_index)
{
	if (pcie_index >= PCIE_INDEX_MAX) {
		pr_info("pcie_index %d not supported !\n", pcie_index);
		return false;
	}
	return g_pcie_linkdown_state[pcie_index] == true ? 0 : 1;
}
EXPORT_SYMBOL(pcie_get_linkup_status);

void *pcie_get_dbi_addr(uint32_t pcie_index)
{
	if (pcie_index >= PCIE_INDEX_MAX) {
		pr_info("pcie_index %d not supported !\n", pcie_index);
		return NULL;
	}

	return g_pcie_ctrl_info[pcie_index].dbi_base;
}

uint32_t pcie_aging_loopback_check(void)
{
	uint32_t res;
	drv_common_file_read_funcptr *drv_common_file_read_func;
	char content[4] = {0}; /* 4 for data */
	static uint32_t g_check_num = 0xffffffff; /* 0xffffffff init value */

	if (g_check_num != 0xffffffff)
		return g_check_num;

	drv_common_file_read_func = get_drv_common_file_read_func_hook();
	if ((*drv_common_file_read_func) == NULL) {
		pr_info("pcie drv_common_file_read_func empty\n");
		return 0;
	}

	res = (*drv_common_file_read_func)(AGEING_LOOPBACK_CHECK_FILE,
		content, sizeof(content));
	if (res != HW_RET_SUCCESS) {
		pr_info("pcie has no test loopback flag\n");
		g_check_num = 0;
		return 0;
	}

	g_check_num = *((uint32_t *)content);
	pr_info("pcie loopback read flag value: %u\n", g_check_num);

	(void)memset(content, 0, sizeof(content));
	res = (*drv_common_file_read_func)(AGEING_LOOPBACK_RESULT_FILE,
			content, sizeof(content));
	if (res == HW_RET_SUCCESS) {
		pr_info("pcie has been loopback result\n");
		g_check_num = 0;
	}
	return g_check_num;
}

static void pcie_aging_loopback_result(char *data, uint32_t data_size)
{
	uint32_t res;
	drv_common_file_write_funcptr *drv_common_file_write_func;

	drv_common_file_write_func =
		get_drv_common_file_write_func_hook();
	if ((*drv_common_file_write_func) == NULL) {
		pr_info("pcie drv_common_file_write_func empty\n");
		return;
	}

	res = (*drv_common_file_write_func)(AGEING_LOOPBACK_RESULT_FILE,
		data, data_size);
	if (res != HW_RET_SUCCESS)
		pr_info("pcie drv_common_file_write_func error\n");
}

static uint32_t pcie_aging_loopback_port(uint32_t testnum, uint32_t port, uint32_t chipid)
{
	uint32_t ret;
	struct dev_pcie_test_s age_loop = {
		.ui_pcie_id = port,
		.ui_pcie_opt = PCIE_TEST_OPT_ENABLE,
		.ui_pcie_size = AGEING_LOOPBACK_TEST_SIZE,
		.ui_pcie_times = testnum,
		.chip_id = chipid,
	};

	ret = pcie_loopback_test(&age_loop);
	if (ret != 0) {
		pr_info("pcie %u age loopback enable error\n", port);
		return ret;
	}

	age_loop.ui_pcie_opt = PCIE_TEST_OPT_START;
	ret = pcie_loopback_test(&age_loop);
	if (ret != 0)
		pr_info("pcie %u age loopback start error\n", port);

	return ret;
}

static uint32_t pcie_aging_loopback_test_res(uint32_t testnum, uint32_t chipid)
{
	uint32_t ret;
	bool is_aging_test = false;
	struct pcie_board_ini_attr *pcie_board_cfg = NULL;
	pcie_get_board_attr(&pcie_board_cfg);
	if ((pcie_board_cfg->pcie_channel_select & PCIE0_MODE_SEL) != 0) {
		pr_info("pcie0 age test select\n");
		if (!g_pcie_linkdown_state[PCIE_INDEX_0]) {
			is_aging_test = true;
			ret = pcie_aging_loopback_port(testnum, PCIE_INDEX_0, chipid);
			if (ret != 0) {
				pr_err("pcie0 age loopback test error\n");
				return AGEING_RESULT_FAIL;
			}
		}
	}

	if ((pcie_board_cfg->pcie_channel_select & PCIE1_MODE_SEL) != 0) {
		pr_info("pcie1 age test select\n");
		if (!g_pcie_linkdown_state[PCIE_INDEX_1]) {
			is_aging_test = true;
			ret = pcie_aging_loopback_port(testnum, PCIE_INDEX_1, chipid);
			if (ret != 0) {
				pr_err("pcie1 age loopback test error\n");
				return AGEING_RESULT_FAIL;
			}
		}
	}

	if ((pcie_board_cfg->pcie_channel_select & PCIE2_MODE_SEL) != 0) {
		pr_info("pcie2 age test select\n");
		if (!g_pcie_linkdown_state[PCIE_INDEX_2]) {
			is_aging_test = true;
			ret = pcie_aging_loopback_port(testnum, PCIE_INDEX_2, chipid);
			if (ret != 0) {
				pr_err("pcie2 age loopback test error\n");
				return AGEING_RESULT_FAIL;
			}
		}
	}

	return is_aging_test ? AGEING_RESULT_SUCC : AGEING_RESULT_NOCHECK;
}

void pcie_aging_loopback_test(uint32_t chipid)
{
	uint32_t testnum;
	uint32_t result;
	drv_common_reset_funcptr *drv_common_reset_func;

	testnum = pcie_aging_loopback_check();
	if (testnum == 0)
		return;

	result = pcie_aging_loopback_test_res(testnum, chipid);
	pr_info("pcie age loopback test res:%u\n", result);
	pcie_aging_loopback_result((char *)&result, sizeof(result));

	drv_common_reset_func = get_drv_common_reset_func_hook();
	if ((drv_common_reset_func != NULL) && ((*drv_common_reset_func) != NULL))
		(*drv_common_reset_func)(NULL, NULL, 0);
}

static bool chipid_check(uint32_t chipid, const uint32_t *pcie_speed, uint32_t reg_state_val)
{
	bool ret = false;

	if (((chipid != HAL_CHIP_ID_5182T) && (pcie_speed[0] != HAL_CHIP_ID_5182T)) ||
	    (chipid == pcie_speed[0]))
		ret = true;
	else
		ret = false;

	return ret ? ((reg_state_val & pcie_speed[1]) == pcie_speed[1]) : false;
}

enum pcie_link_speed pcie_chip_link_speed_calc(uint32_t reg_state_val, uint32_t chipid)
{
	uint32_t ret;
	uint32_t i;
	static const uint32_t chip_pcie_speed[][3] = {
		{ HAL_CHIP_ID_5182T, PCIE_5182T_3D_STATE_VALUE,   PCIE_LINK_SPEED_3D  },
		{ HAL_CHIP_ID_5182T, PCIE_5182T_2D_STATE_VALUE,   PCIE_LINK_SPEED_2D  },
		{ HAL_CHIP_ID_5182T, PCIE_5182T_1D1_STATE_VALUE,  PCIE_LINK_SPEED_1D1 },
		// HW_CHIP_ID_MPW_E matches every chip except 5182t
		{ HW_CHIP_ID_MPW_E,  PCIE_COMMON_2D_STATE_VALUE,  PCIE_LINK_SPEED_2D  },
		{ HW_CHIP_ID_MPW_E,  PCIE_COMMON_1D1_STATE_VALUE, PCIE_LINK_SPEED_1D1 }
	};

	for (i = 0; i < ARRAY_SIZE(chip_pcie_speed); i++) {
		ret = chipid_check(chipid, chip_pcie_speed[i], reg_state_val);
		if (ret)
			return chip_pcie_speed[i][2];
	}
	return PCIE_LINK_SPEED_NO;
}

uint32_t pcie_chip_link_speed_info_get(struct pcie_link_state_info *info,
	uint32_t info_size, uint32_t *pcie_num)
{
	uint32_t chipid;
	uint32_t pcie_index;
	uint32_t i;
	struct pcie_board_ini_attr *pcie_board_cfg = NULL;

	if ((info == NULL) || (pcie_num == NULL))
		return 0;

	chipid = pcie_get_chip_id();
	pcie_get_board_attr(&pcie_board_cfg);
	for (i = 0, pcie_index = 0;
			(pcie_index < PCIE_INDEX_MAX) && (i < info_size); pcie_index++) {
		if ((pcie_board_cfg->pcie_channel_select & (1 << pcie_index)) != 0) {
			uint32_t val_qa;
			uint32_t val_state;

			if ((g_pcie_ctrl_info[pcie_index].misc_base == NULL) ||
			    (g_pcie_ctrl_info[pcie_index].dbi_base == NULL))
				continue;

			pcie_dbi_space_door(chipid,
				g_pcie_misc_base_virt[pcie_index], true);
			info[i].index = pcie_index;
			/* DBI offset 0x80, link status reg */
			val_state = readl(g_pcie_ctrl_info[pcie_index].dbi_base + 0x80);

			info[i].speed = (enum pcie_link_speed)((val_state >> 16) & 0xf);
			if (info[i].speed == PCIE_LINK_SPEED_NO) {
				info[i].width = PCIE_LNK_WIDTH_RESRV;
				info[i].quality = PCIE_LINK_QA_NO;
				pcie_dbi_space_door(chipid,
					g_pcie_misc_base_virt[pcie_index], false);
				continue;
			}
			info[i].width = (enum pcie_link_width)((val_state >> 20) & 0x1f);

			/* DBI offset 0x110, correctable error status register */
			info[i].quality = PCIE_LINK_QA_POOR;
			val_qa = readl(g_pcie_ctrl_info[pcie_index].dbi_base + 0x110);
			if (val_qa == 0)
				info[i].quality = PCIE_LINK_QA_GOOD;

			pcie_dbi_space_door(chipid, g_pcie_misc_base_virt[pcie_index], false);
			i++;
			pr_info("pcie index:%u val_state:%x val_qa:%x\n",
				pcie_index, val_state, val_qa);
		}
	}

	*pcie_num = i;
	return 0;
}
